﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;



namespace AIthin.Neuro
{
   
    [Serializable]
    public class Neuron
    {
        /// <summary>
        /// ident number
        /// </summary>
        public int ID = 0;
        public static List<int> Neurons = new List<int>();

         /// <summary>
        /// Neuron's inputs count.
        /// </summary>
        public int inputsCount = 0;
        /// <summary>
        /// Neuron's inputs count.
        /// </summary>
        public int outputsCount = 0;
        float threshold= 0;
        public Dendrit[] inputs;
        public AxonTerminal[] outputs;
        public bool AllInputsActivated = false;
        public bool Activated = false;
        int DurationBeActiv = 3;

        public Neuron(int Ident, int InputsCount, int OutputsCount)
        {
            ID = Ident;
            Neurons.Add(ID);
            inputsCount = InputsCount;
            outputsCount = OutputsCount;
            inputs = new Dendrit[inputsCount];
            outputs = new AxonTerminal[outputsCount];
            for (int i = 0; i < inputsCount; i++)   inputs[i] = new Dendrit();
            for (int i = 0; i < outputsCount; i++)   outputs[i] = new AxonTerminal();
            threshold = Convert.ToSingle(0.5 * inputsCount);
        }
        public bool Connect(ref Neuron NeuronToConnect)
        {
            // check inputscount
            // check if that neuron is already connected
            foreach (Dendrit d in inputs)
            {
                if (d.ConnectedNeuron == NeuronToConnect.ID)
                {
                    Debug.WriteLine("neuron " + this.ID.ToString() + " double connection to: " + NeuronToConnect.ID.ToString());
                    return false;
                }
            }

            for (int i = 0; i < inputsCount; i++)
            {
                // find free input
                if (inputs[i].ConnectedNeuron == 0)
                {
                    for (int j = 0; j < NeuronToConnect.outputsCount; j++)
                    {
                        //find free output
                        if (NeuronToConnect.outputs[j].ConnectedNeuron == 0)
                        {

                            NeuronToConnect.outputs[j].ConnectedNeuron = this.ID;
                            NeuronToConnect.outputs[j].ConnectedDendrit = i;
                            inputs[i].ConnectedNeuron = NeuronToConnect.ID;
                            inputs[i].ConnectedAxon = j;
                            //
                            Debug.WriteLine("neuron " + this.ID.ToString() + " dendrit " + i.ToString() 
                                + " to: " + NeuronToConnect.ID.ToString() + " axon " + j.ToString());
                            return true;
                        }
                    }
                    //TODO  All outputs are already connected
                    Debug.WriteLine("!!! no free axons");
                    return false;
                }
            }
            //TODO  All inputs are already connected
            Debug.WriteLine("!!! no free dendrits");
            return false;
        }
        /// <summary>
        /// Randomize neuron.
        /// </summary>
        /// 
        /// <remarks>Initialize neuron's inputWeights with random values within the range specified
        /// by <see cref="RandRange"/>.</remarks>
        /// 
        public bool Computate()
        {
            //TODO Check for Ticks and unset Activated for a dendrites
            if (!AllInputsActivated) return false;
            float output = 0;
            foreach (Dendrit d in inputs) output += d.Out();
            float signal = (output > threshold) ? 1.0f : 0.0f;
            //TODO overwriting signals for the outputs neurons replace with learn algoritm
            foreach (AxonTerminal a in outputs)
            {
                a.Signal = signal;
   //             a.ConnectedNeuron
            }
            Debug.Write(this.ID, "neuron ");
            Debug.WriteLine (signal, " - signal");
            return true;
        }
        public bool ReverseComputation()
        {
            
            return true;
        }
        public void ActivateInput(int input)
        {
            inputs[input].Activated = true;
            foreach (Dendrit d in inputs)  if (!d.Activated) return;
            AllInputsActivated = true;
        }
        public void SetInput(double inputSignal)
        {
            //TODO Make input as float
            foreach (Dendrit d in inputs)
            {
                d.Signal = Convert.ToSingle(inputSignal);
                d.Activated = true;
            }
            this.AllInputsActivated = true;
        }
        public void SetOutput(double outputSignal)
        {
            this.Activated = true;
            this.DurationBeActiv = 3;
        }

    }
}
